package com.haiyou.common.binary.reader;

import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.util.Arrays;
import java.util.Collection;
import java.util.Date;
import java.util.Map;

import org.apache.commons.lang3.Validate;

//import com.alibaba.fastjson.JSONArray;
//import com.alibaba.fastjson.JSONObject;
import com.google.common.collect.Maps;
import com.haiyou.common.util.object.ClassUtils;

import lombok.extern.slf4j.Slf4j;

/**
 * 
 * 
 * @Description: 二进制读取工具
 * @author xingyuan
 * @date 2022年2月15日 下午5:04:22
 */
@Slf4j
public class BinaryReader implements Reader {

	private final ByteBuffer buffer;

	public BinaryReader(byte[] array) {
		log.debug("ByteBuffer.wrap:" + array.length + "," + Arrays.toString(array));
		buffer = ByteBuffer.wrap(array);
	}

	@Override
	public ByteBuffer buffer() {

		return buffer;
	}

	@Override
	public boolean hasRemaining() {

		return buffer.hasRemaining();
	}

	@Override
	public int remaining() {

		return buffer.remaining();
	}

	@Override
	public int readByte() {

		return buffer.get();
	}

	@Override
	public int readShort() {

		return buffer.getShort();
	}

	@Override
	public int readInt() {

		return buffer.getInt();
	}

	@Override
	public long readLong() {

		return buffer.getLong();
	}

	@Override
	public float readFloat() {

		return buffer.getFloat();
	}

	@Override
	public double readDouble() {

		return buffer.getDouble();
	}

	@Override
	public boolean readBool() {

		byte b = buffer.get();
		return b == 1 ? true : false;
	}

	@Override
	public char readChar() {

		return buffer.getChar();
	}

	@Override
	public String readString(Charset charset) {

		int len = buffer.getShort();
		byte[] dst = new byte[len];
		buffer.get(dst);
		try {
			return bytes2String(dst, charset);
		} catch (Exception e) {
			throw new RuntimeException(e.getMessage(), e);
		}
	}

	@Override
	public <K, V> Map<K, V> readMap(Class<K> keyClazz, Class<V> valueClazz) {

		Map<K, V> map = Maps.newHashMap();
		int size = readShort();
		for (int i = 0; i < size; i++) {
			K k = null;
			V v = null;
			if (keyClazz == Byte.class) {
				k = (K) new Byte((byte) readByte());
			} else if (keyClazz == Short.class) {
				k = (K) new Short((short) readShort());
			} else if (keyClazz == Integer.class) {
				k = (K) new Integer((int) readInt());
			} else if (keyClazz == Long.class) {
				k = (K) new Long((long) readLong());
			} else if (keyClazz == Float.class) {
				k = (K) new Float((float) readFloat());
			} else if (keyClazz == Double.class) {
				k = (K) new Double((double) readDouble());
			} else if (keyClazz == Character.class) {
				k = (K) new Character((char) readChar());
			} else if (keyClazz == Boolean.class) {
				k = (K) new Boolean((boolean) readBool());
			} else if (keyClazz == String.class) {
				k = (K) readUTF8();
			} else if (ClassUtils.isAssignableFrom(Readable.class, keyClazz)) {
				k = (K) readObject(keyClazz);
			}
			if (valueClazz == Byte.class) {
				v = (V) new Byte((byte) readByte());
			} else if (valueClazz == Short.class) {
				v = (V) new Short((short) readShort());
			} else if (valueClazz == Integer.class) {
				v = (V) new Integer((int) readInt());
			} else if (valueClazz == Long.class) {
				v = (V) new Long((long) readLong());
			} else if (valueClazz == Float.class) {
				v = (V) new Float((float) readFloat());
			} else if (valueClazz == Double.class) {
				v = (V) new Double((double) readDouble());
			} else if (valueClazz == Character.class) {
				v = (V) new Character((char) readChar());
			} else if (valueClazz == Boolean.class) {
				v = (V) new Boolean((boolean) readBool());
			} else if (valueClazz == String.class) {
				v = (V) readUTF8();
			} else if (ClassUtils.isAssignableFrom(Readable.class, valueClazz)) {
				v = (V) readObject(valueClazz);
			}
			map.put(k, v);
		}
		return map;
	}

	@Override
	public <E> Collection<E> readCollection(Class<E> cls, Collection<E> result) {

		if (result == null) {
			throw new RuntimeException(cls.getName());
		}
		int capacity = readShort();
		if (capacity == 0) {
			return result;
		}
		if (cls == Byte.class) {
			for (int i = 0; i < capacity; i++) {
				Byte r = new Byte((byte) readByte());
				result.add((E) r);
			}
			return result;
		} else if (cls == Short.class) {
			for (int i = 0; i < capacity; i++) {
				Short r = new Short((short) readShort());
				result.add((E) r);
			}
			return result;
		} else if (cls == Integer.class) {
			for (int i = 0; i < capacity; i++) {
				Integer r = new Integer((int) readInt());
				result.add((E) r);
			}
			return result;
		} else if (cls == Long.class) {
			for (int i = 0; i < capacity; i++) {
				Long r = new Long((long) readLong());
				result.add((E) r);
			}
			return result;
		} else if (cls == Float.class) {
			for (int i = 0; i < capacity; i++) {
				Float r = new Float((float) readFloat());
				result.add((E) r);
			}
			return result;
		} else if (cls == Double.class) {
			for (int i = 0; i < capacity; i++) {
				Double r = new Double((double) readDouble());
				result.add((E) r);
			}
			return result;
		} else if (cls == Character.class) {
			for (int i = 0; i < capacity; i++) {
				Character r = new Character((char) readChar());
				result.add((E) r);
			}
			return result;
		} else if (cls == Boolean.class) {
			for (int i = 0; i < capacity; i++) {
				Boolean r = new Boolean((boolean) readBool());
				result.add((E) r);
			}
			return result;
		} else if (cls == String.class) {
			for (int i = 0; i < capacity; i++) {
				String r = (String) readUTF8();
				result.add((E) r);
			}
			return result;
		} else if (ClassUtils.isAssignableFrom(Readable.class, cls)) {
			for (int i = 0; i < capacity; i++) {
				Readable r = (Readable) readObject(cls);
				result.add((E) r);
			}
			return result;
		}
		throw new RuntimeException(cls.getName());
	}

	@Override
	public <E> E readObject(Class<E> cls) {

		Validate.isTrue(ClassUtils.isAssignableFrom(Readable.class, cls));

		int len = readShort();
		if (len == 0) {
			return null;
		}
		try {
			E obj = cls.newInstance();
			((Readable) obj).read(this);
			return obj;
		} catch (Exception e) {
			throw new RuntimeException(cls.getName(), e);
		}
	}

	@Override
	public byte[] readBytes() {

		int len = readShort();
		byte[] arr = new byte[len];
		for (int i = 0; i < len; i++) {
			arr[i] = (byte) readByte();
		}
		return arr;
	}

	@Override
	public short[] readShorts() {

		int len = readShort();
		short[] arr = new short[len];
		for (int i = 0; i < len; i++) {
			arr[i] = (short) readShort();
		}
		return arr;
	}

	@Override
	public int[] readInts() {

		int len = readShort();
		int[] arr = new int[len];
		for (int i = 0; i < len; i++) {
			arr[i] = (int) readInt();
		}
		return arr;
	}

	@Override
	public long[] readLongs() {

		int len = readShort();
		long[] arr = new long[len];
		for (int i = 0; i < len; i++) {
			arr[i] = (long) readLong();
		}
		return arr;
	}

	@Override
	public float[] readFloats() {

		int len = readShort();
		float[] arr = new float[len];
		for (int i = 0; i < len; i++) {
			arr[i] = (float) readFloat();
		}
		return arr;
	}

	@Override
	public double[] readDoubles() {

		int len = readShort();
		double[] arr = new double[len];
		for (int i = 0; i < len; i++) {
			arr[i] = (double) readDouble();
		}
		return arr;
	}

	@Override
	public boolean[] readBooleans() {

		int len = readShort();
		boolean[] arr = new boolean[len];
		for (int i = 0; i < len; i++) {
			arr[i] = (boolean) readBool();
		}
		return arr;
	}

	@Override
	public char[] readChars() {

		int len = readShort();
		char[] arr = new char[len];
		for (int i = 0; i < len; i++) {
			arr[i] = (char) readChar();
		}
		return arr;
	}

	@Override
	public String[] readStrings() {

		int len = readShort();
		String[] arr = new String[len];
		for (int i = 0; i < len; i++) {
			arr[i] = (String) readUTF8();
		}
		return arr;
	}

	@Override
	public Byte[] read$Bytes() {

		byte[] _arr = readBytes();
		Byte[] arr = new Byte[_arr.length];
		for (int i = 0; i < arr.length; i++) {
			arr[i] = _arr[i];
		}
		return arr;
	}

	@Override
	public Short[] read$Shorts() {

		short[] _arr = readShorts();
		Short[] arr = new Short[_arr.length];
		for (int i = 0; i < arr.length; i++) {
			arr[i] = _arr[i];
		}
		return arr;
	}

	@Override
	public Integer[] read$Integers() {

		int[] _arr = readInts();
		Integer[] arr = new Integer[_arr.length];
		for (int i = 0; i < arr.length; i++) {
			arr[i] = _arr[i];
		}
		return arr;
	}

	@Override
	public Long[] read$Longs() {

		long[] _arr = readLongs();
		Long[] arr = new Long[_arr.length];
		for (int i = 0; i < arr.length; i++) {
			arr[i] = _arr[i];
		}
		return arr;
	}

	@Override
	public Float[] read$Floats() {

		float[] _arr = readFloats();
		Float[] arr = new Float[_arr.length];
		for (int i = 0; i < arr.length; i++) {
			arr[i] = _arr[i];
		}
		return arr;
	}

	@Override
	public Double[] read$Doubles() {

		double[] _arr = readDoubles();
		Double[] arr = new Double[_arr.length];
		for (int i = 0; i < arr.length; i++) {
			arr[i] = _arr[i];
		}
		return arr;
	}

	@Override
	public Boolean[] read$Booleans() {

		boolean[] _arr = readBooleans();
		Boolean[] arr = new Boolean[_arr.length];
		for (int i = 0; i < arr.length; i++) {
			arr[i] = _arr[i];
		}
		return arr;
	}

	@Override
	public Character[] read$Chars() {

		char[] _arr = readChars();
		Character[] arr = new Character[_arr.length];
		for (int i = 0; i < arr.length; i++) {
			arr[i] = _arr[i];
		}
		return arr;
	}

	@Override
	public Date readDate() {

		long time = readLong();
		if (time == 0) {
			return null;
		}
		return new Date(time);
	}

	@Override
	public Enum readEnum(Class enumClazz) {

		String name = readUTF8();
		if (name == null || name.isEmpty()) {
			return null;
		}
		return Enum.valueOf(enumClazz, name);
	}

	@Override
	public <T> Class<T> readClazz(Class<T> cls) {

		String name = readUTF8();
		if (name == null || name.isEmpty()) {
			return null;
		}
		try {
			return (Class<T>) Class.forName(name);
		} catch (ClassNotFoundException e) {
			throw new RuntimeException(cls.getName(), e);
		}
	}
}
